package com.inno.life.domain.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.inno.life.common.constant.DefaultValueConstant;
import com.inno.life.common.entity.PageResult;
import com.inno.life.common.enums.IsDeletedFlagEnum;
import com.inno.life.common.error.BusinessException;
import com.inno.life.common.utils.DateUtils;
import com.inno.life.domain.convert.MomentConverter;
import com.inno.life.domain.dict.Constant;
import com.inno.life.domain.entity.MomentBO;
import com.inno.life.domain.entity.RecommendUserBO;
import com.inno.life.domain.redis.RedisUtil;
import com.inno.life.domain.service.MomentDomainService;
import com.inno.life.infra.basic.entity.PlannerMoment;
import com.inno.life.infra.basic.entity.PlannerMomentLiked;
import com.inno.life.infra.basic.service.PlannerMomentLikedService;
import com.inno.life.infra.basic.service.PlannerMomentService;
import com.inno.life.infra.entity.UserInfo;
import com.inno.life.infra.rpc.UserRpc;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * @author sean
 */
@Service
@RequiredArgsConstructor
public class MomentDomainServiceImpl implements MomentDomainService {
    private final PlannerMomentService plannerMomentService;
    private final UserRpc userRpc;
    private final PlannerMomentLikedService plannerMomentLikedService;
    private final ThreadPoolTaskExecutor lifeExecutor;
    private final RedisUtil redisUtil;

    @Override
    public void add(MomentBO momentBO) {
        PlannerMoment entity = MomentConverter.INSTANCE.convertBOToEntity(momentBO);


        Date nowDate = DateUtils.getNowDate();

        entity.setId(IdUtil.getSnowflakeNextId());
        entity.setCreateBy(momentBO.getUserId());
        entity.setCreateTime(nowDate);
        entity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode().toString());


        entity.setViewCount(DefaultValueConstant.MOMENT_DEFAULT_COUNT);
        entity.setLikedCount(DefaultValueConstant.MOMENT_DEFAULT_COUNT);
        entity.setCommentCount(DefaultValueConstant.MOMENT_DEFAULT_COUNT);

        this.plannerMomentService.insert(entity);

    }

    @Override
    public PageResult<MomentBO> queryPage(MomentBO momentBO) {
        PlannerMoment entity = MomentConverter.INSTANCE.convertBOToEntity(momentBO);
        entity.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode().toString());
        PageHelper.startPage(momentBO.getPage(), momentBO.getPageSize());
        Page<PlannerMoment> entityPage = this.plannerMomentService.queryPage(entity);
        List<PlannerMoment> entityResult = entityPage.getResult();
        List<MomentBO> boList = MomentConverter.INSTANCE.converEntityListToBOList(entityResult);

        List<Long> userIdList = boList.stream().map(MomentBO::getUserId).toList();
        List<UserInfo> userInfoList = userRpc.getUserInfoByIdList(userIdList);
        Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity()));

        Map<Long, Integer> likedCountMap = this.getLikedCount(boList);
        Map<Long, Boolean> isLikedMap = this.checkIsLiked(boList, momentBO.getCurrentUserId());

        boList.forEach(bo -> {
            UserInfo userInfo = userInfoMap.getOrDefault(bo.getUserId(),new UserInfo());
            bo.setUsername(userInfo.getUsername());
            bo.setAvatar(userInfo.getAvatar());
            Boolean isLiked = isLikedMap.get(bo.getId());
            bo.setLiked(isLiked);
            Integer likedCount = likedCountMap.get(bo.getId());

            bo.setAvatar(userInfo.getAvatar());
            bo.setLikedCount(likedCount);
        });
        return PageResult.of(boList, entityPage.getTotal());
    }

    @Override
    public void del(MomentBO momentBO) {
        PlannerMoment plannerMoment = MomentConverter.INSTANCE.convertBOToEntity(momentBO);
        PlannerMoment moment4db = this.plannerMomentService.queryById(plannerMoment.getId());
        if (Objects.isNull(moment4db) || !moment4db.getUserId().equals(momentBO.getCurrentUserId())){
            throw new BusinessException("操作失败!");
        }
        plannerMoment.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode().toString());
        Date nowDate = DateUtils.getNowDate();
        plannerMoment.setDeletedTime(nowDate);
        Long currentUserId = momentBO.getCurrentUserId();
        plannerMoment.setUpdateBy(currentUserId);
        plannerMoment.setUpdateTime(nowDate);
        this.plannerMomentService.update(plannerMoment);
    }

    @Override
    public void update(MomentBO momentBO) {
        PlannerMoment plannerMoment = MomentConverter.INSTANCE.convertBOToEntity(momentBO);
        PlannerMoment moment4db = this.plannerMomentService.queryById(plannerMoment.getId());
        if (Objects.isNull(moment4db) || !moment4db.getUserId().equals(momentBO.getCurrentUserId())){
            throw new BusinessException("操作失败!");
        }
        Date nowDate = DateUtils.getNowDate();
        Long currentUserId = momentBO.getCurrentUserId();
        plannerMoment.setUpdateBy(currentUserId);
        plannerMoment.setUpdateTime(nowDate);
        this.plannerMomentService.update(plannerMoment);
    }

    @Override
    public MomentBO queryById(MomentBO momentBO) {
        PlannerMoment plannerMoment = MomentConverter.INSTANCE.convertBOToEntity(momentBO);
        PlannerMoment moment4db = this.plannerMomentService.queryById(plannerMoment.getId());
        if (Objects.isNull(moment4db)){
            return new MomentBO();
        }
        MomentBO bo = MomentConverter.INSTANCE.convertEntityToBO(moment4db);
        Long userId = bo.getUserId();
        UserInfo userInfo = userRpc.getUserInfoById(userId);
        bo.setUsername(userInfo.getUsername());
        bo.setAvatar(userInfo.getAvatar());
        Map<Long, Integer> likedCount = this.getLikedCount(Collections.singletonList(bo));
        Integer likeCount = likedCount.get(bo.getId());
        Map<Long, Boolean> isLikedMap = checkIsLiked(Collections.singletonList(bo), momentBO.getCurrentUserId());
        Boolean isLiked = isLikedMap.get(bo.getId());
        bo.setLikedCount(likeCount);
        bo.setLiked(isLiked);
        moment4db = new PlannerMoment();
        moment4db.setId(bo.getId());
        this.plannerMomentService.incrementViewCount(moment4db);
        return bo;
    }

    @Override
    public List<MomentBO> recommend() {
        List<PlannerMoment> moments = this.plannerMomentService.queryRecommend();
        List<MomentBO> boList = MomentConverter.INSTANCE.converEntityListToBOList(moments);
        List<Long> userIdList = boList.stream().map(MomentBO::getUserId).toList();
        List<UserInfo> userInfoList = userRpc.getUserInfoByIdList(userIdList);
        Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity()));
        for (MomentBO bo : boList) {
            UserInfo userInfo = userInfoMap.get(bo.getUserId());
            bo.setUsername(userInfo.getUsername());
        }
        return boList;
    }

    @Override
    public List<RecommendUserBO> recommendUser() {
//        List<Long> userIdList = this.plannerMomentService.qeuryTotalCount();
        return List.of();
    }

    /**
     * 获取点赞数量----redis实现
     */
    private Map<Long, Integer> getLikedCount(List<MomentBO> boList) {
        List<Long> momentIdList = boList.stream().map(MomentBO::getId).toList();
        List<CompletableFuture<Integer>> futureList = momentIdList
                .stream()
                .map(id -> CompletableFuture.supplyAsync(() -> {
                    Object count = redisUtil.get(Constant.CacheKey.MOMENT_LIKED_COUNT + id);
                    if (Objects.isNull(count)) {
                        return 0;
                    } else {
                        return Integer.valueOf(count.toString());
                    }
                })).toList();
        return CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]))
                .thenApply(v -> IntStream.range(0, momentIdList.size())
                        .boxed()
                        .collect(Collectors.toMap(momentIdList::get, i -> futureList.get(i)
                                .join())))
                .join();
    }

    /**
     * 判断是否点赞----redis实现
     */
    private Map<Long, Boolean> checkIsLiked(List<MomentBO> boList, Long userId) {
        List<Long> momentIdList = boList.stream().map(MomentBO::getId).toList();
        List<CompletableFuture<Boolean>> futureList = momentIdList.stream()
                .map(id -> CompletableFuture.supplyAsync(() -> {
                    List<Object> likedInfoList = redisUtil.lGet(Constant.CacheKey.LIKED_CACHE_PREFIX + id);
                    return CollectionUtil.isNotEmpty(likedInfoList)
                            && likedInfoList
                            .stream()
                            .map(l -> Long.valueOf(l.toString())).toList().contains(userId);
                })).toList();
        return CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]))
                .thenApply(v -> IntStream.range(0, momentIdList.size())
                        .boxed()
                        .collect(Collectors.toMap(momentIdList::get, i -> futureList.get(i).join())))
                .join();
    }


    /**
     * 判断是否点赞
     */
    @Deprecated(since = "使用redis存储点赞状态")
    private Map<Long, Boolean> assertIsLiked(List<PlannerMoment> entityResult, Long currentUserId) {
        List<Long> momentIdList = entityResult.stream().map(PlannerMoment::getId).toList();

        CompletableFuture<Long>[] likedCountMap = new CompletableFuture[momentIdList.size()];
        for (int i = 0; i < momentIdList.size(); i++) {
            final int index = i;
            likedCountMap[i] = CompletableFuture.supplyAsync(() -> {
                PlannerMomentLiked plannerMomentLiked = plannerMomentLikedService.queryByFromIdAndToMomentId(currentUserId, momentIdList.get(index));
                return plannerMomentLiked != null ? 1L : 0L;
            }, lifeExecutor);
        }
        return CompletableFuture.allOf(likedCountMap)
                .thenApply(v -> IntStream.range(0, momentIdList.size())
                        .boxed()
                        .collect(Collectors.toMap(momentIdList::get, i -> likedCountMap[i].join() > 0)))
                .join();
    }

    /**
     * 获取点赞数
     */
    @Deprecated(since = "使用redis存储点赞状态")
    private Map<Long, Long> getLikedCountMap(List<PlannerMoment> entityResult) {
        List<Long> momentIdList = entityResult.stream().map(PlannerMoment::getId).toList();
        CompletableFuture<Long>[] likedCountMap = new CompletableFuture[momentIdList.size()];
        for (int i = 0; i < momentIdList.size(); i++) {
            final int index = i;
            likedCountMap[i] = CompletableFuture.supplyAsync(() -> plannerMomentLikedService.queryCountByToMomentId(momentIdList.get(index)), lifeExecutor);
        }

        // 等待所有异步任务完成，并将结果合并为一个Map
        return CompletableFuture.allOf(likedCountMap)
                .thenApply(v -> IntStream.range(0, momentIdList.size())
                        .boxed()
                        .collect(Collectors.toMap(momentIdList::get, i -> likedCountMap[i].join())))
                .join();
    }
}
